#ifdef USE_SQL
#include <string.h>
#include <stdio.h>

#include "lib.h"
#include "lirserv.h"
#include "lirconn.h"
#include "lirhand.h"
#include "lirsql.h"
#include "lirsaved.h"
#include "lirgame.h"
#include "lirproto.h"
#include "stdncerr.h"
#include "opts.h"
#include "log.h"

#include "growbuf.h"
#include "smtp.h"

#include "XMLTranslator.h"

typedef struct _info
{
	String			login;
	PID				pid;
	SESSIONID		sessionId;
	LIRGameID		gid;
	bool8			forMoney;
} Info, *pInfo;

void
LIRServer::startTracker()
{
	ServerOpts::load();

	tracker = new LIRSQL(ServerOpts::dbdsn,
		ServerOpts::dblogin, ServerOpts::dbpasswd);

	// CSN-8891
	webServiceCall = new WebServiceCall();
}

void
LIRServer::stopTracker()
{
	delete tracker;
	tracker = 0;

	delete webServiceCall;
	webServiceCall = 0;
}

bool8
LIRServer::initSuccess()
{
	if (tracker && tracker->initOK())
		return Server::initSuccess();
	else
	{
		Log::msgError("Failed to connect to the database.  Aborting.");
		return false;
	}
}

bool8 LIRConnection::getForRealFlag()
{
	pInfo info = (pInfo)casinoSpecific;

    if (info && info->forMoney)
        return true;
    else 
        return false;
}

bool8
LIRConnection::parseCasinoLogin(int32 stdReq, int32* Err)
{
	Info *	info;
	char	login[PIDLEN + 1];
	int32	i;

	if (msgbuf[1]	!= APP1				|| msgbuf[2] != APP2 ||
		msgbuf[3]	!= PROTO_VERSION1	|| msgbuf[4] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c  ",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

	info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	i = stdReq;

	memcpy(login, msgbuf + i, PIDLEN);
	login[PIDLEN] = 0;
	i += PIDLEN;

	strlwr(login);
	info->login = login;

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{		
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
	
	if (tracker)
	{
		//int32 result;
		bool8 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
		
	}

	info->pid = info->login;

	/*
	if (tracker)
	{
		int32 result = LIRSQL::ExecFailed;

		tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId,
					info->pid);
		tracker->unlock();

		if (result != LIRSQL::NoError)
		{
			if(result == LIRSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == LIRSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

			DEBUGMSG(("Login: attempt to play without logging in: %S",
				(const uint16*) info->login));
			delete info;
			return false;
		}
	}
	*/

	memset(info->gid, 0xff, sizeof(LIRGameID));

	casinoSpecific = info;

    #ifdef FORCEDSHOE 
    m_pid=info->login;
    #endif


	return true;
}

bool8
LIRConnection::parseUnicodeCasinoLogin(int32 stdReq, int32* Err)
{
	Info *	info;
	int32	i;

	if (msgbuf[2]	!= APP1				|| msgbuf[3] != APP2 ||
		msgbuf[4]	!= PROTO_VERSION1	|| msgbuf[5] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c  ",
				msgbuf[2],msgbuf[3],msgbuf[4],msgbuf[5]));
		return false;
	}

	info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	i = stdReq;

	i += info->login.deserialize(msgbuf + i);
	info->login.toLower();

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}
	else
	{	
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
	
	if (tracker)
	{
		//int32 result;
		bool8 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			return false;
		}
		
	}

	info->pid = info->login;

	/*
	if (tracker)
	{
		int32 result = LIRSQL::ExecFailed;

		tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId,
					info->pid);
		tracker->unlock();

		if (result != LIRSQL::NoError)
		{
			if(result == LIRSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == LIRSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

			DEBUGMSG(("Login: attempt to play without logging in: %S",
				(const uint16*) info->login));
			delete info;
			return false;
		}
	}
	*/

	memset(info->gid, 0xff, sizeof(LIRGameID));

	casinoSpecific = info;

    #ifdef FORCEDSHOE 
    m_pid=info->login;
    #endif


	return true;
}

void
LIRConnection::removeCasinoSpecific()
{
	delete (pInfo)casinoSpecific;

	casinoSpecific = (void*)0;
}

int32
LIRConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
LIRConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

bool8	
LIRConnection::trackGetBalance( int32 *balance )
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;
	int32 comp;
	*balance = 0;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->getBalance(ServerOpts::casinoId,
			info->pid, info->forMoney, balance, &comp);
		tracker->unlock();
	}

	return (bool8)(result == LIRSQL::NoError);
}

int32	
LIRConnection::trackAdjustBalance(int32 Amount)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;
	int32 bal=0, comp=0;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->adjustBalance(ServerOpts::casinoId,
			info->pid, info->forMoney,
			Amount, &bal, &comp);
		tracker->unlock();
	}

    if (result == LIRSQL::NoError)
		return bal;
	return 0;
}

bool8
LIRConnection::trackGetPlayerLimits(int32* minBet, int32* maxBet,
	int32* compFactor)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->getPlayerLimits(info->pid, GAMEID, MACHID,
			minBet, maxBet, compFactor);

		tracker->unlock();
	}

    return (bool8)(result == LIRSQL::NoError);
}

bool8
LIRConnection::trackNewGame(int32 Bet, int32 progressiveBet, int32 multiBankBet, int32 *Balance)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;
	int32 comp;

	if (tracker && info)
	{
		tracker->lock();
		result = ((LIRSQL*)tracker)->createGame(ServerOpts::casinoId,
			info->pid, info->forMoney, info->sessionId,
			info->forMoney ? compFactor : 0,
			info->gid, Bet, progressiveBet, multiBankBet, Balance, 
			&comp, this->payTable);
		tracker->unlock();
	}

    return (bool8)(result == LIRSQL::NoError);
}


bool8 LIRConnection::trackDeal(int32 nDeckSeed, int32 numPlayer, uint8* player)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock();
        // kharmon.  10-16-06.  #8226.  Don't store the dealer cards in the record until the game is over.
		result = ((LIRSQL*)tracker)->deal(info->pid, info->gid, nDeckSeed, numPlayer, player);
		tracker->unlock();
	}
    return (bool8)(result == LIRSQL::NoError);
}

bool8	
LIRConnection::trackGameDone(bool8 Raised, int32 payouts[3], int32 payProgressive,
							 int32 payMultiBank, int32 Outcome, int32 BalAdjust, 
							 bool8 bMalfunction, int32 *Balance,    // kharmon.  01-05-06.  #4250.  Added bMalfunction functionality for verifying large progressive jackpot wins.
                             LIRHand* Dealer, LIRHand* Player)      // kharmon.  10-16-06.  #8226.  Don't store the cards in the record until the game is over.
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;
	int32 comp;

	if (tracker && info)
	{
		tracker->lock();
		result = ((LIRSQL*)tracker)->gameDone(ServerOpts::casinoId,
			info->pid, info->forMoney, info->gid,
			Raised, payouts, payProgressive, payMultiBank, 
			Outcome, BalAdjust, bMalfunction, Balance, &comp,
            Dealer->getCount(), Dealer->getCards(),
			Player->getCount(), Player->getCards());
		tracker->unlock();
	}

    return (bool8)(result == LIRSQL::NoError);
}

bool8	
LIRConnection::trackTurnOneDone(bool8 Raised, LIRHand* Dealer)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock();
        // kharmon.  11-07-06.  #8226.  Now we will store the visible cards in the database, but not the facedown cards.
        uint8 aucDealer[2];
        memset(aucDealer, 0, sizeof(aucDealer));
        aucDealer[0] = Dealer->getCards()[0];
		result = ((LIRSQL*)tracker)->turnOneDone(info->pid, info->forMoney, info->gid,	Raised, 2, aucDealer);
		tracker->unlock();
	}

    return (bool8)(result == LIRSQL::NoError);
}


bool8	
LIRConnection::trackHasSavedGame(int32* err)
{
	pInfo info = (pInfo)casinoSpecific;
	LIRSavedGame sg;
	int32 result = LIRSQL::NoError;
	bool8 exists = LIRSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock();
		result = ((LIRSQL*)tracker)->hasSavedGame(info->pid, info->forMoney,
			info->gid, &sg, &exists);
		if (result == LIRSQL::NoError)
		{
			if ( exists)
			{
				game = new LIRGame(this, &sg);

			}
			else
			{
				memset(info->gid, 0xff, sizeof(LIRGameID));
			}
		}
		tracker->unlock();
	}

	if (game && !game->isGameOK())
		*err = LOGINERR_CONTACTCUSTSERVICE;
	else if (result != LIRSQL::NoError)
		*err = LOGINERR_TRYLATER;
	else
		*err = 0;

	return (bool8)((result == LIRSQL::NoError) && game && game->isGameOK()) ;
}

bool8	
LIRConnection::trackSaveGame()
{
	pInfo info = (pInfo)casinoSpecific;
	LIRSavedGame sg;
	int32 result;

	if (!game)
		return true;

	if (tracker && info)
	{
		game->save(&sg);

		tracker->lock();
		result = ((LIRSQL*)tracker)->saveGame(info->pid, info->forMoney, info->gid, &sg);
		tracker->unlock();

		return (bool8)(result == LIRSQL::NoError);
	}
	return false;
}

bool8	
LIRConnection::trackGetOptions()
{
    uint8 realProgBetPcnt = 0;
	uint8 funProgBetPcnt = 0;
	bool8 progressiveOn = false;
	int32 jackpotID	= -1;
	int32 progressiveFixedAmountMultiplier = 1;

	int32 result = LIRSQL::ExecFailed;

	if (tracker)
	{
		tracker->lock();
		result = ((LIRSQL*)tracker)->getOptions(this->payTable, progressiveOn,
						this->progPayTable, realProgBetPcnt, funProgBetPcnt, jackpotID, progressiveFixedAmountMultiplier);
		tracker->unlock();
		
		setProgressiveJackpotID(jackpotID);
		setProgressiveOn(progressiveOn);
		setProgressiveFixedAmountMultiplier(progressiveFixedAmountMultiplier);
		
		/* realProgBetPcnt and funProgBetPcnt are no longer needed... */

		return (bool8)(result == LIRSQL::NoError);
	}
	return false;
}

bool8
LIRConnection::trackRemoveSavedGame()
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = LIRSQL::ExecFailed;

	if (tracker)
	{
		tracker->lock();
		result = ((LIRSQL*)tracker)->removeSavedGame(info->pid, info->forMoney);
		tracker->unlock();

		return (bool8)(result == LIRSQL::NoError);
	}
	return false;
}

int32
LIRConnection::trackValidateLogin()
{
    pInfo info = (pInfo)casinoSpecific;
    int32 result = LIRSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId, info->pid);
        tracker->unlock();
    }

    return result;
}

int32
LIRConnection::trackValidateSession()
{
    pInfo info = (pInfo)casinoSpecific;
    int32 result = LIRSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateSession(info->pid, info->sessionId);
        tracker->unlock();
    }

    return result;
}

void LIRConnection::trackLogoutSession()
{
	pInfo info = (pInfo)casinoSpecific;
	
	if (tracker && info && !info->login.isEmpty())
    {
        tracker->lock();
		tracker->logout(ServerOpts::casinoId, info->login, info->forMoney, String::emptyString, info->sessionId);
        tracker->unlock();
    }
}

void
LIRConnection::SendWinMail(int32 win, int32 jackpotAmount)
{
	String msg = "This is only a possible jackpot, as signaled by a casino server.\r\n"
				 "You will receive another email confirming or denying this potential jackpot.\r\n"
				 "Until then, this information is Bodog confidential, and not for release to clients or the public.\r\n"
				 "\r\nHere is the information about the potential win and winner - \r\n";
	
	pInfo info = (pInfo)casinoSpecific;
	String buffer;
	buffer.sprintf("Casino: %S\r\nPid: %S\r\n", (const uint16 *)ServerOpts::casinoName, (const uint16 *)info->pid);
	msg += buffer;

	buffer.sprintf("Game: %s\r\n", "Let 'Em Ride");
	msg += buffer;

	buffer.sprintf("Win: %d%% progressive\r\nGame num: %s\r\n", win, info->gid);
	msg += buffer;

	buffer.sprintf("Amount: $%d.%02d\r\n", jackpotAmount/100, jackpotAmount%100);
	msg += buffer;

	SendJackpotCandidateMail(ServerOpts::smtphost,
							 "Let 'Em Ride",
							 ServerOpts::casinoName,
							 msg,
							 getForRealFlag());
}


#else	// USE_SQL

#include <string.h>

#include "lib.h"
#include "lirserv.h"
#include "lirconn.h"
#include "lirver.h"
#include "stdncerr.h"

#define APP1		(uint8)'L'
#define APP2		(uint8)'R'

typedef struct _info
{
	int32 balance;
} Info, *pInfo;

void LIRServer::startTracker() 
{ 
	ServerOpts::load();

	tracker = new PGPokerSQL(ServerOpts::dbdsn,
		ServerOpts::dblogin, ServerOpts::dbpasswd);
}
void LIRServer::stopTracker() 
{
	delete tracker;
	tracker = 0;
}
bool8 LIRServer::initSuccess() { return Server::initSuccess(); }

bool8
LIRConnection::parseCasinoLogin(int32 /*stdReq*/, int32* Err)
{
	if (msgbuf[1]	!= APP1				|| msgbuf[2] != APP2 ||
		msgbuf[3]	!= PROTO_VERSION1	|| msgbuf[4] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		return false;
	}

	Info* info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	info->balance = 100000;		// $1000.00 to start with

	casinoSpecific = info;

	return true;
}
void
LIRConnection::removeCasinoSpecific()
{
	delete (pInfo)casinoSpecific;

	casinoSpecific = (void*)0;
}

int32
LIRConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
LIRConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32	
LIRConnection::trackGetBalance()
{
	pInfo info = (pInfo)casinoSpecific;
	if (info)
		return info->balance;
	else
		return 0;
}

int32	
LIRConnection::trackAdjustBalance(int32 Amount)
{
	pInfo info = (pInfo)casinoSpecific;
	if (info)
	{
		info->balance += Amount;

		return info->balance;
	}
	else
		return 0;
}

bool8
LIRConnection::trackGetPlayerLimits(int32* minBet, int32* maxBet)
{
	*minBet = 500;
	*maxBet = 20000;
	return true;
}

bool8
LIRConnection::trackNewGame(int32 Bet, int32 progressiveBet, int32* Balance, uint8 payTable)
{
	pInfo info = (pInfo)casinoSpecific;

	if (info)
		*Balance = trackAdjustBalance(-(Bet+progressiveBet));
	else
		*Balance = 0;

	return true;
}

bool8	
LIRConnection::trackDeal(LIRHand* /*Dealer*/, LIRHand* /*Player*/)
{
	return true;
}

bool8	
LIRConnection::trackGameDone(bool8 /*Raised*/,
	int32 payouts[3], int32 /*Outcome*/,
	int32 BalAdjust, int32 *Balance)
{
	pInfo info = (pInfo)casinoSpecific;

	if (info)
		*Balance = trackAdjustBalance(BalAdjust);
	else
		*Balance = 0;

	return true;
}

bool8 LIRConnection::trackTurnOneDone(bool8 /*Raised*/) {	return true; }
bool8 LIRConnection::trackHasSavedGame(int32* /*err*/) { return false; }
bool8 LIRConnection::trackSaveGame() { return true; }

#endif
